using System;
using System.Collections.Generic;
using System.Linq;
using FireStarter.Base;
using Firestarter.DB.Schema.BO;
using FireStarterModeller.BO;
using Habanero.Base;
using Habanero.Base.Exceptions;

namespace Firestarter.DB_DomainModel.Logic
{
    public class DBToDMUniqueConstraintImporter : IDBToDMUniqueConstraintImporter
    {
        public DMUniqueConstraint Import(DMClass dmClass, DBKey dbKey, out IList<string> errorMessages)
        {
            errorMessages = new List<string>();
            if (dmClass == null) throw new ArgumentNullException("dmClass");
            if (dbKey == null) throw new ArgumentNullException("dbKey");
            if (dmClass == null)
                throw new HabaneroArgumentException
                    ("_dmClass", "You cannot import the primary key since the business object is null");

            DMUniqueConstraint dmUniqueConstraint =  FindUniqueConstraint(dmClass, dbKey);
            dmUniqueConstraint.UniqueConstraintName = dbKey.KeyName;

            foreach (DMUniqueConstraintProperty uniqueConstraintProperty in GetBusinessObjectArray(dmUniqueConstraint.UniqueConstraintProperties))
            {
                var foundUCColumn = FindKeyColumn(dbKey.KeyColumns, uniqueConstraintProperty);
                if (foundUCColumn == null || ApprovedDelete(foundUCColumn))
                {
                    uniqueConstraintProperty.MarkForDelete();
                    //dmUniqueConstraint.UniqueConstraintProperties.Remove(uniqueConstraintProperty);
                }
            }

            foreach (DBKeyColumn keyColumn in GetBusinessObjectArray(dbKey.KeyColumns))
            {
                var dbColumn = keyColumn.Column;
                if(dbColumn == null)
                {
                    AppendColumnMissingErrMessage(dmClass, dbKey, keyColumn, errorMessages);
                    continue;
                }
                var ucProp = FindUCProperty(dmUniqueConstraint.UniqueConstraintProperties, keyColumn);
                if (ucProp == null && !ApprovedDelete(keyColumn))
                {
                    DMProperty property = dbColumn.FindBOProperty(dmClass);
                    if (property == null)
                    {
                        AppendErrMessage(dmClass, dbKey, keyColumn, errorMessages);
                        continue;
                    }
                    dmUniqueConstraint.AddPropertyInfo(property);
                }
                if (ApprovedDelete(keyColumn))
                {
                    keyColumn.MarkForDelete();
                    if (ucProp != null) ucProp.MarkForDelete();
                }
                keyColumn.PendingChangeType = ChangeType.None;
            }
            
            return dmUniqueConstraint;

        }

        private IBusinessObject[] GetBusinessObjectArray(IBusinessObjectCollection fromBOCol)
        {
            IBusinessObject[] businessObjects = new IBusinessObject[fromBOCol.Count];
            fromBOCol.CopyTo(businessObjects, 0);
            return businessObjects;
        }
        private static void AppendColumnMissingErrMessage(DMClass dmClass, DBKey dbKey, DBKeyColumn columnInfo, IList<string> errorMessages)
        {
            string expectedMessage = string.Format("The Unique Constraint '{0}' for business object '{1}' could not be "
                                                   + "created since the column '{2}' does not exist on the DB PK",
                                                   dbKey.KeyName, dmClass.ClassNameBO, columnInfo.ColumnName);

            errorMessages.Add(expectedMessage);
            
        }
        private static void AppendErrMessage(DMClass dmClass, DBKey dbKey, DBKeyColumn columnInfo, IList<string> errorMessages)
        {
            string expectedMessage = string.Format("The Unique Constraint '{0}' for business object '{1}' could not be "
                                                   + "created since the property for column '{2}' does not exist on the business object",
                                                   dbKey.KeyName, dmClass.ClassNameBO, columnInfo.ColumnName);

            errorMessages.Add(expectedMessage);
            
        }

        private bool ApprovedDelete(IMergableBo column)
        {
            return column.PendingChangeType == ChangeType.Delete && column.PendingChangeApproved;
        }

        public DBKeyColumn FindKeyColumn(IEnumerable<DBKeyColumn> keyColumns, DMUniqueConstraintProperty ucProp)
        {
            string propertyName = ucProp.PropertyName;
            var mappedDBColumnName = ucProp.Property.MappedDBColumnName;
            var mappedDBColumnID = ucProp.Property.MappedDBColumnID;
            var keyColumn = keyColumns.FirstOrDefault(column => column.ColumnID == mappedDBColumnID);
            
            if(keyColumn == null)
            {
                keyColumn = keyColumns.FirstOrDefault(column => column.ColumnName == mappedDBColumnName);
            }
            if (keyColumn == null)
            {
                keyColumn = keyColumns.FirstOrDefault(column => column.GetPropertyName() == propertyName);
            }
            return keyColumn;
        }

        public DMUniqueConstraintProperty FindUCProperty(IEnumerable<DMUniqueConstraintProperty> ucProps, DBKeyColumn keyColumn)
        {
            //return ucProps.FirstOrDefault(prop => prop.PropertyName == propertyName);

            string propertyName = keyColumn.GetPropertyName();
            var mappedDBColumnName = keyColumn.ColumnName;
            var mappedDBColumnID = keyColumn.ColumnID;
            var ucProp = ucProps.FirstOrDefault(prop => prop.Property.MappedDBColumnID == mappedDBColumnID);

            if (ucProp == null)
            {
                ucProp = ucProps.FirstOrDefault(prop => prop.Property.MappedDBColumnName == mappedDBColumnName);
            }
            if (ucProp == null)
            {
                ucProp = ucProps.FirstOrDefault(prop => prop.PropertyName == propertyName);
            }
            return ucProp;
        }
        private DMUniqueConstraint FindUniqueConstraint(DMClass dmClass, DBKey dbKey)
        {
            DMUniqueConstraint uniqueConstraint = dbKey.GetMappedDMUniqueConstraint();
            if (uniqueConstraint != null && uniqueConstraint.Class != dmClass)
            {
                uniqueConstraint.MappedDBKey = null;
                uniqueConstraint = null;
            }
            if(uniqueConstraint == null)
            {
                uniqueConstraint = dmClass.UniqueConstraints.Find(constraint => constraint.UniqueConstraintName == dbKey.KeyName);
            }
            if (uniqueConstraint == null)
            {
                uniqueConstraint = new DMUniqueConstraint {
                    Class = dmClass,
                    MappedDBKey = dbKey
                };
            }
            return uniqueConstraint;
        }

        //private DMObjectIdentity GetMappedObjectIdentity(DBPrimaryKey dbPrimaryKey)
        //{
        //    DMObjectIdentity objectIdentity = dbPrimaryKey.GetMappedDMObjectIdentity();
        //    if (objectIdentity != null && objectIdentity.Class != _dmClass)
        //    {
        //        objectIdentity.MappedDBPrimaryKey = null;// objectIdentity.Class.MappedTable.PrimaryKeyDB;
        //        objectIdentity = null;
        //    }
        //    if (objectIdentity == null)
        //    {
        //        objectIdentity = _dmClass.ObjectIdentity ?? new DMObjectIdentity { Class = _dmClass };
        //    }
        //    objectIdentity.MappedDBPrimaryKey = dbPrimaryKey;
        //    return objectIdentity;
        //}
    }
}